#include "stdafx.h"

#include <MyFC/ToolBar.h>
#include <MyFC/Utility.h>
#include <xtd/functional>
#include <string>
#include <algorithm>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

namespace My {

ToolButton::ToolButton(ToolBar & owner, unsigned int cmd, const char * szGlyph) :
	m_Owner(owner)
{
	m_Command = cmd;
	std::string mask = szGlyph;
	mask += "_MASK";

	m_Glyph.Load(szGlyph, mask.c_str());
	m_Grayed.Load(szGlyph, mask.c_str());

	m_Enabled = true;
	m_Check = Unchecked;

	CString s;
	s.LoadString(m_Command);
	m_Hint = s;

	m_Owner.Add(this);
}

ToolButton::ToolButton(ToolBar & owner, unsigned int cmd, const char * szGlyph, const char * szMask) :
	m_Owner(owner)
{
	m_Command = cmd;

	m_Glyph.Load(szGlyph, szMask);
	m_Grayed.Load(szGlyph, szMask);

	m_Enabled = true;
	m_Check = Unchecked;

	CString s;
	s.LoadString(m_Command);
	m_Hint = s;

	m_Owner.Add(this);
}

CRect ToolButton::Bounds() const
{
	CRect r(CPoint(0, 0), m_Owner.m_ButtonSize);
	switch (m_Owner.m_Orientation) {
	case ToolBar::HORIZONTAL:
		r.OffsetRect(m_Offset, 0);
		break;
	case ToolBar::VERTICAL:
		r.OffsetRect(0, m_Offset);
		break;
	}
	return r;
}

void ToolButton::HandlePaint(CDC * pDC, const CRect & r)
{
	CRect rClient = ClientRect();
	COLORREF cF = ::GetSysColor(COLOR_BTNFACE);
	COLORREF cL = ::GetSysColor(COLOR_BTNHILIGHT);
	COLORREF cS = ::GetSysColor(COLOR_BTNSHADOW);
	COLORREF cLF = MeanColor(cL, cF);

	bool isDown = (GetState() == Button::Down || m_Check == Checked);
	bool isGrayed = (!m_Enabled || m_Check == Indeterminate);
	COLORREF cGround = (GetState() == Button::Down || m_Check != Unchecked ? cLF : cF);

	CBrush bGround(cGround);
	pDC->FillRect(&rClient, &bGround);
	CPoint p = rClient.CenterPoint();
	if (isDown) {
		p += CPoint(1, 1);
		Draw3dRect(pDC, rClient, cF, cLF);
	} else {
		Draw3dRect(pDC, rClient, cL, cS);
	}
	if (isGrayed)
		m_Grayed.Draw(pDC, p.x, p.y, ALIGN_CENTER);
	else
		m_Glyph.Draw(pDC, p.x, p.y, ALIGN_CENTER);
}

void ToolButton::HandleUpdateUI()
{
	ToolButtonCmdUI state(* this);

	state.m_nID = m_Command;

	CWnd * pTarget = GetWindow();
	if (!pTarget->IsFrameWnd())
		pTarget = pTarget->GetParentFrame();

	state.DoUpdate(static_cast<CFrameWnd *>(pTarget), TRUE);
}

void ToolButton::SetEnabled(bool on)
{
	if (on != m_Enabled) {
		m_Enabled = on;
		Invalidate();
		UpdateState();
	}
}

void ToolButton::SetHint(const String & hint)
{
	if (hint != m_Hint) {
		m_Hint = hint;
		UpdateHint();
	}
}

void ToolButton::SetCheck(CheckState s)
{
	if (s != m_Check) {
		m_Check = s;
		Invalidate();
	}
}

void ToolButton::HandleClick()
{
	AfxGetMainWnd()->SendMessage(WM_COMMAND, m_Command, 0);
	HandleUpdateUI();
}

// ToolButtonCmdUI

void ToolButtonCmdUI::Enable(BOOL bOn)
{	 
	m_Owner.SetEnabled(bOn != 0);
	m_bEnableChanged = true;
}

void ToolButtonCmdUI::SetCheck(int nCheck)
{
	ToolButton::CheckState states[] = {
		ToolButton::Unchecked,
		ToolButton::Checked,
		ToolButton::Indeterminate
	};
	m_Owner.SetCheck(states[nCheck]);
}

void ToolButtonCmdUI::SetRadio(BOOL bOn)
{
	m_Owner.SetCheck(bOn ? ToolButton::Checked : ToolButton::Unchecked);
}

void ToolButtonCmdUI::SetText(LPCTSTR lpszText)
{
	m_Owner.SetHint(lpszText);
}

// ToolBar

ToolBar::ToolBar(Orientation o)
{
	m_Orientation = o;
	m_ButtonSize = CSize(0, 0);
	m_Length = 0;
	m_Gap = 6;
}

ToolBar::~ToolBar()
{
	std::for_each(m_Buttons.begin(), m_Buttons.end(),
		xtd::deleter());
}

void ToolBar::UpdateButtonOffsets()
{
	m_Length = 0;
	for (unsigned int i = 0; i < m_Buttons.size(); ++i) {
		if (m_Buttons[i] == 0)
			m_Length += m_Gap;
		else {
			m_Buttons[i]->m_Offset = m_Length;
			m_Length += m_Orientation == HORIZONTAL ? 
				m_ButtonSize.cx : m_ButtonSize.cy;
		}
	}
	Invalidate();
}

CSize ToolBar::GetPreferredSize() const
{
	return m_Orientation == HORIZONTAL ?
		CSize(m_Length, m_ButtonSize.cy) : CSize(m_ButtonSize.cx, m_Length);
}

void ToolBar::SetButtons(const ToolButtonInfo items[], unsigned int count)
{
	std::for_each(m_Buttons.begin(), m_Buttons.end(),
		xtd::deleter());
	m_Buttons.resize(count);
	for (unsigned int i = 0; i < count; ++i) {
		if (items[i].id == 0)
			m_Buttons[i] = 0;
		else
			m_Buttons[i] = new ToolButton(* this, items[i].id, items[i].glyph);
	}
	UpdateButtonOffsets();
}

void ToolBar::AddButton(unsigned int nID, const char * szGlyph)
{
	ToolButton * ptr = 0;
	if (nID != 0)
		ptr = new ToolButton(* this, nID, szGlyph);
	m_Buttons.push_back(ptr);
	UpdateButtonOffsets();
}

void ToolBar::AddButton(unsigned int nID, const char * szGlyph, const char * szMask)
{
	ToolButton * ptr = 0;
	if (nID != 0)
		ptr = new ToolButton(* this, nID, szGlyph, szMask);
	m_Buttons.push_back(ptr);
	UpdateButtonOffsets();
}

void ToolBar::InsertButton(My::Size pos, unsigned int nID, const char * szGlyph, const char * szMask)
{
	ToolButton * ptr = 0;
	if (nID != 0)
		ptr = new ToolButton(* this, nID, szGlyph, szMask);
	m_Buttons.insert(m_Buttons.begin() + pos, ptr);
	UpdateButtonOffsets();
}

void ToolBar::AddSeparator()
{
	m_Buttons.push_back(0);
	UpdateButtonOffsets();
}

void ToolBar::SetButtonSize(CSize s)
{
	if (s != m_ButtonSize) {
		m_ButtonSize = s;
		UpdateButtonOffsets();
	}
}

void ToolBar::SetGap(int gap)
{
	if (gap != m_Gap) {
		m_Gap = gap;
		UpdateButtonOffsets();
	}
}

void ToolBar::HandlePaint(CDC * pDC, const CRect & r)
{
	COLORREF cL = ::GetSysColor(COLOR_BTNHILIGHT);
	COLORREF cS = ::GetSysColor(COLOR_BTNSHADOW);

	int start, end;

	bool block = false;
	for (unsigned int i = 0; i < m_Buttons.size(); ++i) {
		if (!block && m_Buttons[i] != 0) {
			start = m_Buttons[i]->m_Offset;
			block = true;
		}
		if (block && ((i + 1) == m_Buttons.size() || m_Buttons[i + 1] == 0)) {
			end = m_Buttons[i]->m_Offset + 
				(m_Orientation == HORIZONTAL ? m_ButtonSize.cx : m_ButtonSize.cy); 
			CRect r(CPoint(0, 0), m_ButtonSize);
			switch (m_Orientation) {
			case HORIZONTAL:
				r.left = start;
				r.right = end;
				break;
			case VERTICAL:
				r.top = start;
				r.bottom = end;
				break;
			}		
			r.InflateRect(1, 1, 1, 1);
			Draw3dRect(pDC, r, cS, cL);
			block = false;
		}
	}
}


} // My
